import { Injectable, inject } from '@angular/core';
import { TransformService } from '@/home/service/transform.service';
import { EditTrackLine } from '../data-model/edit-track-line.model';
import { g } from './global-data'

@Injectable({
    providedIn: 'root'
})
export class TrackLineService {
    transformService = inject(TransformService);

    compute_track_line_args(track_line: EditTrackLine) {
        let track_points;
        if (!!track_line.points) {
            track_points = track_line.points;
        } else {
            track_points = g.track_line_points_cache.get(track_line.uuid);
        }

        const lon_list = track_points.map((point: any[]) => point[0]);
        const lat_list = track_points.map((point: any[]) => point[1]);
        const alt_list = track_points.map((point: any[]) => point[2]);
        const fpx_list = track_points.map((point: any[]) => point[4]);
        const fpy_list = track_points.map((point: any[]) => point[5]);

        track_line.lon_min = Math.min(...lon_list);
        track_line.lon_max = Math.max(...lon_list);
        track_line.lat_min = Math.min(...lat_list);
        track_line.lat_max = Math.max(...lat_list);
        track_line.alt_min = Math.min(...alt_list);
        track_line.alt_max = Math.max(...alt_list);
        track_line.fpx_min = Math.min(...fpx_list);
        track_line.fpx_max = Math.max(...fpx_list);
        track_line.fpy_min = Math.min(...fpy_list);
        track_line.fpy_max = Math.max(...fpy_list);

        track_line.point_num = track_points.length;

        this.compute_track_line_thumbnail(track_line);
        this.compute_track_line_distance(track_line);
        this.compute_track_line_climb_and_descent(track_line);
        this.compute_track_line_time_args(track_line);
    }

    compute_track_line_thumbnail(track_line: EditTrackLine) {
        let fpx_range = track_line.fpx_max! - track_line.fpx_min! + 1;
        let fpy_range = track_line.fpy_max! - track_line.fpy_min! + 1;
        let fpx_base, fpy_base, ratio;

        if (fpx_range > fpy_range) {
            fpx_base = track_line.fpx_min!;
            ratio = fpx_range >> 4;  // 除以16
            const rem = fpx_range % 16;
            if (rem > 0) {
                ratio += 1;
                fpx_range += (16 - rem);
                fpx_base -= (16 - rem) >> 1;
            }

            fpy_base = track_line.fpy_min! - ((fpx_range - fpy_range) >> 1);
        } else {
            fpy_base = track_line.fpy_min!;
            ratio = fpy_range >> 4;
            const rem = fpy_range % 16;
            if (rem > 0) {
                ratio += 1;
                fpy_range += (16 - rem);
                fpy_base -= (16 - rem) >> 1;
            }

            fpx_base = track_line.fpx_min! - ((fpy_range - fpx_range) >> 1);
        }

        let track_points;
        if (!!track_line.points) {
            track_points = track_line.points;
        } else {
            track_points = g.track_line_points_cache.get(track_line.uuid);
        }


        let pre_px = Math.floor((track_points[0][4] - fpx_base) / ratio);
        let pre_py = Math.floor((track_points[0][5] - fpy_base) / ratio);

        const thumbnail_points = [[pre_px, pre_py]];
        for (let i = 1; i < track_points.length; i++) {
            const point = track_points[i];
            const px = Math.floor((point[4] - fpx_base) / ratio);
            const py = Math.floor((point[5] - fpy_base) / ratio);
            if (px !== pre_px || py !== pre_py) {
                thumbnail_points.push([px, py]);
                pre_px = px;
                pre_py = py;
            }
        }

        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext('2d');
        canvas.width = 16;
        canvas.height = 16;
        ctx!.lineWidth = 1;
        ctx!.strokeStyle = `rgba(${track_line.red},${track_line.green},${track_line.blue},${track_line.alpha})`;
        ctx!.beginPath();
        ctx!.moveTo(thumbnail_points[0][0], thumbnail_points[0][1])
        for (let i = 1; i < thumbnail_points.length; i++) {
            ctx!.lineTo(thumbnail_points[i][0], thumbnail_points[i][1]);
        }
        ctx!.stroke();
        track_line.thumbnail = canvas.toDataURL('image/png');
    }

    compute_track_line_distance(track_line: EditTrackLine) {
        let track_points;
        if (!!track_line.points) {
            track_points = track_line.points;
        } else {
            track_points = g.track_line_points_cache.get(track_line.uuid);
        }

        const {distance, distance_horizon} = this.compute_distance(track_points, 0, track_points.length - 1);

        track_line.distance = distance;
        track_line.distance_horizon = distance_horizon
    }

    compute_distance(track_points: any, start_idx: number, end_idx: number) {
        if (end_idx <= start_idx) {
            return {
                distance: 0,
                distance_horizon: 0,
            }
        }

        let distance_horizon = 0;
        let distance_with_slope = 0;
        let lon1 = track_points[start_idx][0];
        let lat1 = track_points[start_idx][1];
        let alt1 = track_points[start_idx][2];

        for (let i = start_idx + 1; i < end_idx + 1; i++) {
            const lon2 = track_points[i][0];
            const lat2 = track_points[i][1];
            const alt2 = track_points[i][2];
            const delta_radian = this.transformService.compute_radian_between(lon1, lat1, lon2, lat2);
            const delta_km = delta_radian * g.EARTH_RADIUS_KM;
            const delta_vertical = Math.abs(alt2 - alt1) / 1000;
            const delta_with_slope = Math.pow(delta_km * delta_km + delta_vertical * delta_vertical, 0.5);
            distance_horizon += delta_km;
            distance_with_slope += delta_with_slope;
            lon1 = lon2;
            lat1 = lat2;
            alt1 = alt2;
        }

        return {
            distance: parseFloat(distance_with_slope.toFixed(2)),
            distance_horizon: parseFloat(distance_horizon.toFixed(2)),
        }
    }

    compute_track_line_climb_and_descent(track_line: EditTrackLine) {
        let track_points;
        if (!!track_line.points) {
            track_points = track_line.points;
        } else {
            track_points = g.track_line_points_cache.get(track_line.uuid);
        }

        const {climb, descent} = this.compute_climb_and_descent(track_points, 0, track_points.length - 1);

        track_line.climb = climb;
        track_line.descent =descent;
    }

    compute_climb_and_descent(track_points: any, start_idx: number, end_idx: number) {
        if (end_idx <= start_idx) {
            return {
                climb: 0,
                descent: 0,
            }
        }

        let climb = 0.0;
        let descent = 0.0;
        let got_pre_minimum = false;
        let got_pre_maximum = false;
        let pre_extreme = 0.0;
        let alt_pre = track_points[start_idx][2];

        for (let i = start_idx + 1; i < end_idx + 1; i++) {
            const alt_now = track_points[i][2];
            const locally_up = alt_now > alt_pre;
            const overall_up = got_pre_minimum && (alt_pre > pre_extreme);
            const overall_dn = got_pre_maximum && (alt_pre < pre_extreme);
            const more_than_wiggle = Math.abs(alt_now - alt_pre) > g.ALT_WIGGLE_LIMIT;

            if (!got_pre_minimum && !got_pre_maximum) {
                if (more_than_wiggle) {
                    if (locally_up) {
                        got_pre_minimum = true;
                    } else {
                        got_pre_maximum = true;
                    }
                    pre_extreme = alt_pre;
                    alt_pre = alt_now;
                }
            } else if (overall_up) {
                if (locally_up) {
                    alt_pre = alt_now;
                } else if (more_than_wiggle) {
                    climb += (alt_pre - pre_extreme);
                    pre_extreme = alt_pre;
                    got_pre_minimum = false;
                    got_pre_maximum = true;
                    alt_pre = alt_now;
                }
            } else if (overall_dn) {
                if (locally_up) {
                    if (more_than_wiggle) {
                        descent += (pre_extreme - alt_pre);
                        pre_extreme = alt_pre;
                        got_pre_minimum = true;
                        got_pre_maximum = false;
                        alt_pre = alt_now  ;
                    }
                } else {
                    alt_pre = alt_now;
                }
            }
        }

        // 最后一段
        if (got_pre_minimum && alt_pre > pre_extreme) {
            climb += (alt_pre - pre_extreme);
        }
        if (got_pre_maximum && alt_pre < pre_extreme) {
            descent += (pre_extreme - alt_pre);
        }

        return {
            climb: Math.floor(climb),
            descent: Math.floor(descent)
        }
    }

    compute_track_line_time_args(track_line: EditTrackLine) {
        if (!track_line.has_timestamp) return;

        let track_points;
        if (!!track_line.points) {
            track_points = track_line.points;
        } else {
            track_points = g.track_line_points_cache.get(track_line.uuid);
        }

        track_line.time_begin = this.utc2local(track_points[0][3]);
        track_line.time_end = this.utc2local(track_points[track_points.length - 1][3]);

        const time_begin: any = new Date(track_points[0][3]);
        const time_end: any = new Date(track_points[track_points.length - 1][3]);
        const duration: number = time_end - time_begin;
        let seconds = Math.floor(duration / 1000);
        let minutes = Math.floor(seconds / 60);
        let hours = Math.floor(minutes / 60);
        let days = Math.floor(hours / 24);

        seconds %= 60;
        minutes %= 60;
        hours %= 24;

        const day_str = days > 0 ? days + '天' : ''
        track_line.duration = `${day_str}${hours}:${minutes}:${seconds}`;
    }

    utc2local(utc: string) {
        const date: any = new Date(utc);

        const y = date.getFullYear();
        const m = date.getMonth() + 1;
        const m_str = m < 10 ? ('0' + m) : m;
        const d = date.getDate();
        const d_str = d < 10 ? ('0' + d) : d;
        const h = date.getHours();
        const h_str = h < 10 ? ('0' + h) : h;
        const minute = date.getMinutes();
        const second = date.getSeconds();
        const minute_str = minute < 10 ? ('0' + minute) : minute;
        const second_str = second < 10 ? ('0' + second) : second;

        return y + '-' + m_str + '-' + d_str + ' ' + h_str + ':' + minute_str + ':' + second_str;
    }

    compute_edit_track_line_args(track_line: EditTrackLine) {
        const start_idx = track_line.sel_start_idx;
        const end_idx = track_line.sel_end_idx;

        if (end_idx! < start_idx!) {
            return;
        }

        track_line.sel_point_num = end_idx! - start_idx! + 1;

        track_line.sel_alt_max = 0;
        track_line.sel_alt_min = 99999;

        for (let i = start_idx; i! < end_idx! + 1; i!++) {
            const point = track_line.points![i!];
            if (point[2] > track_line.sel_alt_max!) {
                track_line.sel_alt_max = point[2];
            }
            if (point[2] < track_line.sel_alt_min!) {
                track_line.sel_alt_min = point[2];
            }
        }

        const {distance, distance_horizon} = this.compute_distance(track_line.points, track_line.sel_start_idx!, track_line.sel_end_idx!);
        track_line.sel_distance = distance;
        track_line.sel_distance_horizon = distance_horizon;

        const {climb, descent} = this.compute_climb_and_descent(track_line.points, track_line.sel_start_idx!, track_line.sel_end_idx!);
        track_line.sel_climb = climb;
        track_line.sel_descent = descent;

        this.compute_edit_track_line_time_args(track_line);
    }

    compute_edit_track_line_time_args(track_line: EditTrackLine) {
        if (!track_line.has_timestamp) return;

        const track_points = track_line.points!;


        track_line.sel_time_begin = this.utc2local(track_points[track_line.sel_start_idx!][3]);
        track_line.sel_time_end = this.utc2local(track_points[track_line.sel_end_idx!][3]);

        const time_begin: any = new Date(track_points[track_line.sel_start_idx!][3]);
        const time_end: any = new Date(track_points[track_line.sel_end_idx!][3]);
        const duration: number = time_end - time_begin;
        let seconds = Math.floor(duration / 1000);
        let minutes = Math.floor(seconds / 60);
        let hours = Math.floor(minutes / 60);
        let days = Math.floor(hours / 24);

        seconds %= 60;
        minutes %= 60;
        hours %= 24;

        const day_str = days > 0 ? days + '天' : ''
        track_line.sel_duration = `${day_str}${hours}:${minutes}:${seconds}`;

        if (track_line.selected_point! >= 0) {
            track_line.selected_point_time = this.utc2local(track_points[track_line.selected_point!][3]);
        }
    }

    delete_edit_track_line_extra_fields(track_line: EditTrackLine) {
        delete track_line.points;
        delete track_line.selected;
        delete track_line.checked;
        delete track_line.selected_point;
        delete track_line.sel_start_idx;
        delete track_line.sel_end_idx;
        delete track_line.sel_alt_min;
        delete track_line.sel_alt_max;
        delete track_line.sel_distance;
        delete track_line.sel_distance_horizon;
        delete track_line.sel_climb;
        delete track_line.sel_descent;
        delete track_line.sel_time_begin;
        delete track_line.sel_time_end;
        delete track_line.sel_duration;
        delete track_line.sel_point_num;
        delete track_line.selected_point_time;
    }

    change_track_line_thumbnail_color(track_line: EditTrackLine) {
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext('2d')!;
        canvas.width = 16;
        canvas.height = 16;

        const img = new Image();
        img.src = track_line.thumbnail!;
        img.onload = () => {
            ctx.drawImage(img, 0, 0);

            // 获取图像数据
            const image_data = ctx.getImageData(0, 0, 16, 16);
            const data = image_data.data;

            // 遍历所有像素
            for (let i = 0; i < data.length; i += 4) {
                // 检查alpha通道（透明度）
                 const alpha = data[i + 3];

                // 如果像素不是完全透明的
                if (alpha > 0) {
                    // 替换颜色但保持原始透明度
                    data[i] = track_line.red!;     // 红色
                    data[i + 1] = track_line.green!; // 绿色
                    data[i + 2] = track_line.blue!; // 蓝色
                    // data[i + 3] 保持原始alpha值
                }
            }

            // 将处理后的图像数据放回Canvas
            ctx.putImageData(image_data, 0, 0);

            track_line.thumbnail = canvas.toDataURL('image/png');
        };
    }

}
